#include <poll.h>
#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Log.hpp"

#define IP "101.34.209.208"
#define DEFAULT_PORT 8080

const int pnum = 1024;

/*select的优缺点：
    优点: select只负责等待，可以等待多个fd。相对于多进程与多线程，IO效率更高，不使用多进程多线程也能同时与多个客户端交互。
    缺点: 
    1. 每次需要重置参数fd_set
    2. 需要第三方数组的参与，会出现大量遍历操作
    3. OS需要不断从用户态切换到内核态，在两态之间进行拷贝工作
    4. 需要OS遍历所有关心的文件描述符，来判断是否就绪，也是max_fd的作用
    5. fd_set是系统提供的位图类型，大小是固定的。能管理的fd有上限。
*/

class PollServer
{
public:
    PollServer(int port = DEFAULT_PORT) : _sock(new TcpSocket(port)), _num(pnum)
    {
        _sock->BulidListenSocket(port);
        _rfds = new struct pollfd[_num];
        for(int i = 0; i < _num; i++)
        {
            _rfds[i].fd = -1;
            _rfds[i].events = 0;
            _rfds[i].revents = 0;
        }
        _rfds[0].fd = _sock->GetSocket();
        _rfds[0].events = POLLIN;
    }

    void Handle()
    {
        for(int i = 0; i < _num; i++)
        {
            // 关心的fd有事件就绪
            if(_rfds[i].fd >= 0 && _rfds[i].revents & POLLIN)
            {
                int fd = _rfds[i].fd;
                // 监听套接字
                if(fd == _sock->GetSocket())
                {
                    std::string peerip;
                    uint16_t peerport;
                    int newfd = _sock->AcceptSocket(&peerip, &peerport);
                    // 把获取的套接字添加到_rfds中
                    for(int i = 0; i < _num; i++)
                    {
                        if(_rfds[i].fd == -1)
                        {
                            _rfds[i].fd = newfd;
                            _rfds[i].events = POLLIN;
                            _rfds[i].revents = 0;
                            break;
                        }
                    }
                }
                // 普通套接字
                else
                {
                    int fd = _rfds[i].fd;
                    char buffer[1024];
                    int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
                    if(n > 0)
                    {
                        buffer[n] = '\0';
                        ILOG("client# %s", buffer);
                    }
                    else if(n <= 0)
                    {
                        ELOG("client close or recv error ...");
                        close(fd);
                        _rfds[i].fd = -1;
                        _rfds[i].events = 0;
                        _rfds[i].revents = 0;
                    }
                }
            }
        }
    }

    void Loop()
    {
        while(1)
        {
            int timeout = 1000;
            int ret = poll(_rfds, _num, timeout);
            if(ret < 0)
            {
                ELOG("select error ...");
                exit(1);
            }
            else if(ret > 0)
            {
                DLOG("剩余时间:");
            }
            else
            {
                // DLOG("timeout ...");
            }
            Handle();
        }
    }


private:
    Socket* _sock;
    struct pollfd *_rfds;
    int _num;
};